﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace IronCow.UnitTests
{
    [TestFixture]
    public class TestDateConverter
    {
        [Test]
        public void TestToday()
        {
            var expected = new FuzzyDateTime(DateTime.Today, false);
            var actual = DateConverter.ParseDateTime("today", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("tod", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("Today", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestTomorrow()
        {
            var expected = new FuzzyDateTime(DateTime.Today.AddDays(1), false);
            var actual = DateConverter.ParseDateTime("tomorrow", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("tom", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("Tomorrow", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestShortDay()
        {
            var expectedDt = new DateTime(DateTime.Today.Year, 4, 25);
            if (expectedDt < DateTime.Today)
                expectedDt = expectedDt.AddYears(1);
            var expected = new FuzzyDateTime(expectedDt, false);

            var actual = DateConverter.ParseDateTime("Apr 25", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("apr 25", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("25 Apr", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("25 apr", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestLongDay()
        {
            var expected = new FuzzyDateTime(new DateTime(2008, 4, 25), false);
            var actual = DateConverter.ParseDateTime("2008/04/25", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("2008-04-25", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestAmericanFormats()
        {
            var expected = new FuzzyDateTime(new DateTime(2008, 4, 25), false);
            var actual = DateConverter.ParseDateTime("04/25/2008", DateFormat.American);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestEuropeanFormats()
        {
            var expected = new FuzzyDateTime(new DateTime(2008, 4, 25), false);
            var actual = DateConverter.ParseDateTime("25/04/2008", DateFormat.European);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestSpecificDay()
        {
            var today = DateTime.Today;
            var expectedDt = new DateTime(today.Year, today.Month, 25);
            if (expectedDt < today)
                expectedDt = expectedDt.AddMonths(1);
            var expected = new FuzzyDateTime(expectedDt, false);

            var actual = DateConverter.ParseDateTime("25th", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestEndOfMonth()
        {
            var today = DateTime.Today;
            var expectedDt = new DateTime(today.Year, today.Month, 1);
            expectedDt = expectedDt.AddMonths(1).AddDays(-1);
            var expected = new FuzzyDateTime(expectedDt, false);

            var actual = DateConverter.ParseDateTime("end of month", DateFormat.Default);
            Assert.AreEqual(expected, actual);

            actual = DateConverter.ParseDateTime("End of  month", DateFormat.Default);
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void TestDayOfWeek()
        {
            var expected = DateTime.Today;
            var actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1); 
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }

        [Test]
        public void TestNextDayOfWeek()
        {
            var expected = DateTime.Today.AddDays(7);
            var actual = DateConverter.ParseDateTime("next " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("Next " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("next " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("next " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("Next  " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("next  " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = expected.AddDays(1);
            actual = DateConverter.ParseDateTime("next " + expected.DayOfWeek.ToString(), DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }

        [Test]
        public void TestDayOfWeekAndTime()
        {
            var expected = DateTime.Today.AddDays(3).AddHours(18);
            var actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString() + " at 6pm", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);

            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString() + "@6pm", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);

            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString() + " at 18:00", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);

            actual = DateConverter.ParseDateTime(expected.DayOfWeek.ToString() + " @ 6:00pm", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);
        }

        [Test]
        public void TestTime()
        {
            var expected = DateTime.Today.AddHours(18);
            if (expected < DateTime.Now)
                expected = expected.AddDays(1);
            var actual = DateConverter.ParseDateTime("6pm", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);

            actual = DateConverter.ParseDateTime("18:00", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);

            actual = DateConverter.ParseDateTime("18", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, true), actual);
        }

        [Test]
        public void TestMinutesOffset()
        {
            // Here we test that the actual dates are within 1 second of the expected dates
            // because a few milliseconds can have passed between the 2 instructions.
            var expected = DateTime.Now.AddMinutes(35);
            var actual = DateConverter.ParseDateTime("35 minutes", DateFormat.Default);
            Assert.LessOrEqual(expected - actual.DateTime, new TimeSpan(0, 0, 1));

            expected = DateTime.Now.AddMinutes(35);
            actual = DateConverter.ParseDateTime("35 min", DateFormat.Default);
            Assert.LessOrEqual(expected - actual.DateTime, new TimeSpan(0, 0, 1));
        }

        [Test]
        public void TestHoursOffset()
        {
            // Here we test that the actual dates are within 1 second of the expected dates
            // because a few milliseconds can have passed between the 2 instructions.
            var actual = DateConverter.ParseDateTime("1h", DateFormat.Default);
            var expected = DateTime.Now.AddHours(1);
            Assert.LessOrEqual(expected - actual.DateTime, new TimeSpan(0, 0, 1));

            actual = DateConverter.ParseDateTime("1 hour", DateFormat.Default);
            expected = DateTime.Now.AddHours(1);
            Assert.LessOrEqual(expected - actual.DateTime, new TimeSpan(0, 0, 1));

            actual = DateConverter.ParseDateTime("5 hours", DateFormat.Default);
            expected = DateTime.Now.AddHours(5);
            Assert.LessOrEqual(expected - actual.DateTime, new TimeSpan(0, 0, 1));
        }

        [Test]
        public void TestDaysOffset()
        {
            var expected = DateTime.Today.AddDays(1);
            var actual = DateConverter.ParseDateTime("1 day", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = DateTime.Today.AddDays(5);
            actual = DateConverter.ParseDateTime("5 days", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }

        [Test]
        public void TestWeeksOffset()
        {
            var expected = DateTime.Today.AddDays(1 * 7);
            var actual = DateConverter.ParseDateTime("1 week", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            actual = DateConverter.ParseDateTime("1w", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = DateTime.Today.AddDays(3 * 7);
            actual = DateConverter.ParseDateTime("3 weeks", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }

        [Test]
        public void TestMonthsOffset()
        {
            var expected = DateTime.Today.AddMonths(1);
            var actual = DateConverter.ParseDateTime("1 month", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = DateTime.Today.AddMonths(3);
            actual = DateConverter.ParseDateTime("3 months", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }

        [Test]
        public void TestYearsOffset()
        {
            var expected = DateTime.Today.AddYears(1);
            var actual = DateConverter.ParseDateTime("1 year", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);

            expected = DateTime.Today.AddYears(3);
            actual = DateConverter.ParseDateTime("3 years", DateFormat.Default);
            Assert.AreEqual(new FuzzyDateTime(expected, false), actual);
        }
    }
}
